#pragma once

#pragma managed

#include "Unmanaged/Database.h"
#include "Unmanaged/File.h"
#include "Convert.h"

using namespace System;
using namespace System::Collections;
using namespace System::Collections::Generic;
using namespace System::IO;
using namespace System::Threading;
using namespace FlexminAPI;

namespace USI 
{

public ref class Shots
{
private:
	int _shot1;
	int _shot2;

public:

	property int Shot1
	{
		int get()
		{
			return _shot1;
		}
	}

	property int Shot2
	{
		int get()
		{
			return _shot2;
		}
	}

	Shots(int shot1, int shot2)
	{
		_shot1 = shot1;
		_shot2 = shot2;
	}
};

public ref class ResultRecord
{
private:
	String ^_reason;
	String ^_warnLevel;
	String ^_path;
	String ^_name;

public:

	property String ^Reason
	{
		String ^get()
		{
			return _reason;
		}
	}

	property String ^WranLevel
	{
		String ^get()
		{
			return _warnLevel;
		}
	}

	property String ^Path
	{
		String ^get()
		{
			return _path;
		}
	}

	property String ^Name
	{
		String ^get()
		{
			return _name;
		}
	}

	ResultRecord(String ^reason, String ^warnLevel, String ^path, String ^name)
	{
		_reason = reason;
		_warnLevel = warnLevel;
		_path = path;
		_name = name;
	}
};


public ref class USISlave : public ISlave
{
private:
	static USISlave ^__this;

	bool wip;
	UInt32 work;
	List<ResultRecord ^> ^results;

public:
	static USISlave ^GetThis()
	{
		return __this;
	}

	USISlave()
	{
		__this = this;

		wip = true;
		results = gcnew List<ResultRecord ^>();
	}

	bool GetWip()
	{
		Monitor::Enter(this);
		bool _wip = wip;
		Monitor::Exit(this);

		return _wip;
	}

	array<String ^, 2> ^GetResults()
	{
		Monitor::Enter(this);
		if (results->Count == 0)
		{
			Monitor::Exit(this);
			return nullptr;
		}
		array<String ^, 2> ^res = gcnew array<String ^, 2>(results->Count, 4);
		for (int iRes = 0; iRes < results->Count; iRes++)
		{
			res[iRes, 0] = results[iRes]->Reason;
			res[iRes, 1] = results[iRes]->WranLevel;
			res[iRes, 2] = results[iRes]->Path;
			res[iRes, 3] = results[iRes]->Name;
		}
		results = gcnew List<ResultRecord ^>();
		Monitor::Exit(this);

		return res;
	}

	void AddResult(ResultRecord ^res)
	{
		Monitor::Enter(this);
		results->Add(res);
		Monitor::Exit(this);
	}

	UInt32 GetWork()
	{
		Monitor::Enter(this);
		UInt32 _work = work;
		Monitor::Exit(this);

		return _work;
	}

	void AddWork(UInt32 amount)
	{
		Monitor::Enter(this);
		work += amount;
		Monitor::Exit(this);
	}

	System::Void SaveListDefault(array<String ^> ^list)
	{
		SaveList(list, "default.list");
	}

	array<String ^> ^LoadListDefault()
	{
		return LoadList("default.list");
	}

	System::Void SaveList(array<String ^> ^list, String ^path)
	{
		StreamWriter ^writer;
		try
		{
			writer = gcnew StreamWriter(path);
		}
		catch (...)
		{
			return;
		}

		for (int iLine = 0; iLine < list->Length; iLine++)
		{
			writer->WriteLine(list[iLine]);
		}

		writer->Close();
	}

	array<String ^> ^LoadList(String ^path)
	{
		StreamReader ^reader;
		try
		{
			reader = gcnew StreamReader(path);
		}
		catch (...)
		{
			return gcnew array<String ^>(0);
		}

		List<String ^> ^list = gcnew List<String ^>();

		while (!reader->EndOfStream)
		{
			String ^str = reader->ReadLine();
			if (str->Length > 0)
				list->Add(str);
		}

		reader->Close();

		return list->ToArray();
	}

	array<DirectoryInfo ^> ^GetDirectories(String ^path)
	{
		array<String ^> ^dirPaths = Directory::GetDirectories(path);
		int size = dirPaths->Length;

		array<DirectoryInfo ^> ^result = gcnew array<DirectoryInfo ^>(size);

		for (int iDir = 0; iDir < size; iDir++)
		{
			result[iDir] = gcnew DirectoryInfo(dirPaths[iDir]);
		}

		return result;
	}

	array<FileInfo ^> ^GetFiles(String ^path)
	{
		array<String ^> ^filesPaths = Directory::GetFiles(path);
		int size = filesPaths->Length;

		array<FileInfo ^> ^result = gcnew array<FileInfo ^>(size);

		for (int iDir = 0; iDir < size; iDir++)
		{
			result[iDir] = gcnew FileInfo(filesPaths[iDir]);
		}

		return result;
	}

	UInt32 ExpireDirs(array<String ^> ^dirs)
	{
		std::vector<_tstring> paths;
		for (int iDir = 0; iDir < dirs->Length; iDir++)
		{
			paths.push_back(Converter::ToTString(dirs[iDir]));
		}

		return ::ExpireDirs(paths);
	}

	String ^GetFullPath(String ^path)
	{
		return System::IO::Path::GetFullPath(path);
	}

	System::Void SelectDB(String ^path)
	{
		_tstring File = Converter::ToTString(path);
		settings.db = File;
		db.Clear();
		db.Attach(File.c_str());
	}

	array<String ^> ^GetShots()
	{
		size_t count = db.getNum();

		array<String ^> ^shots = gcnew array<String ^>(count);

		TCHAR str[256];
		SYSTEMTIME st;
		for (size_t iShot = 0; iShot < count; iShot++)
		{
			st = db.getTime(iShot);
			_stprintf(str, _T("%02d.%02d.%04d %02d:%02d:%02d"), st.wDay, st.wMonth, st.wYear, st.wHour, st.wMinute, st.wSecond);
			shots[iShot] = Converter::ToNetString(str);
		}

		return shots;
	}

	System::Void Initialize()
	{
		InitDB();
		db.Clear();
		db.Attach(settings.db.data());
	}

	System::Void Deinitialize()
	{
		db.Clear();
		WriteSettings();
	}
	
	System::Void MakeShotThread(Object ^_paths)
	{
		Monitor::Enter(this);
		wip = true;
		Monitor::Exit(this);

		array<String ^> ^dirs = dynamic_cast<array<String ^> ^>(_paths);

		std::vector<_tstring> paths;
		for (int iDir = 0; iDir < dirs->Length; iDir++)
		{
			paths.push_back(Converter::ToTString(dirs[iDir]));
		}

		db.MakeShot(paths);

		Monitor::Enter(this);
		wip = false;
		Monitor::Exit(this);
	}

	System::Void MakeShot(array<String ^> ^paths)
	{
		Thread ^thread = gcnew Thread(gcnew ParameterizedThreadStart(this, &USISlave::MakeShotThread));
		thread->Start(paths);
	}

	System::Void ResetWork()
	{
		Monitor::Enter(this);
		work = 0;
		Monitor::Exit(this);
	}
	
	System::Void CompareShots(int shot1, int shot2)
	{
		
		FILETIME uliTime1, uliTime2;
		SYSTEMTIME t1 = db.getTime(shot1);
		SYSTEMTIME t2 = db.getTime(shot2);
		SystemTimeToFileTime(&t1, &uliTime1);
		SystemTimeToFileTime(&t2, &uliTime2);

		Shots ^shots;

		if (CompareFileTime(&uliTime1, &uliTime2) < 0)
			shots = gcnew Shots(shot1, shot2);
		else
			shots = gcnew Shots(shot2, shot1);

		Monitor::Enter(this);
		wip = true;
		Monitor::Exit(this);

		db.getShot(shots->Shot1)->Compare(db.getShot(shots->Shot2));

		Monitor::Enter(this);
		
		wip = false;
		Monitor::Exit(this);
	}

	int GetPointWeight()
	{
		return settings.bCRC ? CHECK_WDH_CRC : CHECK_WDH;
	}

	String ^GetWorkDir()
	{
		return Converter::ToNetString(GetWorkDirectory().c_str());
	}


	System::Void SetCRC(bool crc)
	{
		settings.bCRC = crc;
	}

	bool GetCRC()
	{
		return settings.bCRC;
	}

	System::Void SetCriticalExts(array<String ^> ^exts)
	{
		settings.dext.clear();
		for (int iExt = 0; iExt < exts->Length; iExt++)
		{
			settings.dext.push_back(Converter::ToTString(exts[iExt]));
		}
	}

	array<String ^> ^GetCriticalExts()
	{
		size_t count = settings.dext.size();

		array<String ^> ^exts = gcnew array<String ^>(count);

		for (size_t iExt = 0; iExt < count; iExt++)
		{
			exts[iExt] = Converter::ToNetString(settings.dext[iExt].data());
		}

		return exts;
	}

	System::Void SetWarningExts(array<String ^> ^exts)
	{
		settings.wext.clear();
		for (int iExt = 0; iExt < exts->Length; iExt++)
		{
			settings.wext.push_back(Converter::ToTString(exts[iExt]));
		}
	}

	array<String ^> ^GetWarningExts()
	{
		size_t count = settings.wext.size();

		array<String ^> ^exts = gcnew array<String ^>(count);

		for (size_t iExt = 0; iExt < count; iExt++)
		{
			exts[iExt] = Converter::ToNetString(settings.wext[iExt].data());
		}

		return exts;
	}

	System::Void SetIgnoreExts(array<String ^> ^exts)
	{
		settings.iext.clear();
		for (int iExt = 0; iExt < exts->Length; iExt++)
		{
			settings.iext.push_back(Converter::ToTString(exts[iExt]));
		}
	}

	array<String ^> ^GetIgnoreExts()
	{
		size_t count = settings.iext.size();

		array<String ^> ^exts = gcnew array<String ^>(count);

		for (size_t iExt = 0; iExt < count; iExt++)
		{
			exts[iExt] = Converter::ToNetString(settings.iext[iExt].data());
		}

		return exts;
	}

		
	System::Void SetCriticalPaths(array<String ^> ^paths)
	{
		settings.dpath.clear();
		for (int iExt = 0; iExt < paths->Length; iExt++)
		{
			settings.dpath.push_back(Converter::ToTString(paths[iExt]));
		}
	}

	array<String ^> ^GetCriticalPaths()
	{
		size_t count = settings.dpath.size();

		array<String ^> ^paths = gcnew array<String ^>(count);

		for (size_t iExt = 0; iExt < count; iExt++)
		{
			paths[iExt] = Converter::ToNetString(settings.dpath[iExt].data());
		}

		return paths;
	}

	System::Void SetWarningPaths(array<String ^> ^paths)
	{
		settings.wpath.clear();
		for (int iExt = 0; iExt < paths->Length; iExt++)
		{
			settings.wpath.push_back(Converter::ToTString(paths[iExt]));
		}
	}

	array<String ^> ^GetWarningPaths()
	{
		size_t count = settings.wpath.size();

		array<String ^> ^paths = gcnew array<String ^>(count);

		for (size_t iExt = 0; iExt < count; iExt++)
		{
			paths[iExt] = Converter::ToNetString(settings.wpath[iExt].data());
		}

		return paths;
	}

	System::Void SetIgnorePaths(array<String ^> ^paths)
	{
		settings.ipath.clear();
		for (int iExt = 0; iExt < paths->Length; iExt++)
		{
			settings.ipath.push_back(Converter::ToTString(paths[iExt]));
		}
	}

	array<String ^> ^GetIgnorePaths()
	{
		size_t count = settings.ipath.size();

		array<String ^> ^paths = gcnew array<String ^>(count);

		for (size_t iExt = 0; iExt < count; iExt++)
		{
			paths[iExt] = Converter::ToNetString(settings.ipath[iExt].data());
		}

		return paths;
	}

	System::Void LoadShot(int iShot)
	{
		if (!db.getShot(iShot)->IsOpen())
			db.getShot(iShot)->Open();
	}
};

}
